﻿using DotNetExtensions.AspNet.Http.Events;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DotNetExtensions.AspNet.Http
{
    public class HttpClientFactory : Singleton<HttpClientFactory>
    {
        public enum HttpClientKind
        {
            Restful = 0,
            File = 1
        }
        private readonly ConcurrentBag<IHttpClient> httpClents;
        private HttpClientFactory()
        {
            httpClents = new ConcurrentBag<IHttpClient>();
        }
        public List<IHttpClient> HttpClents { get { return httpClents.ToList(); } }

        public IHttpClient Create(HttpClientKind kind)
        {
            lock (mutex)
            {
                IHttpClient client = null;
                if (httpClents.Any(h => !h.Busying))
                {
                    client = httpClents.Where(h => h.Busying == false && h.Kind == kind).FirstOrDefault();
                }

                if (client == null)
                {
                    client = Create((int)kind);
                    httpClents.Add(client);
                }

                return client;
            }
        }

        public T Create<T>() where T : class, IHttpClient, new()
        {
            lock (mutex)
            {
                T client = null;
                if (httpClents.Any(h => !h.Busying))
                {
                    client = (T)httpClents.Where(h => h.Busying == false && typeof(T) == h.GetType()).FirstOrDefault();
                }

                if (client == null)
                {
                    client = new T();
                    httpClents.Add(client);
                }

                return client;
            }
        }

        private IHttpClient Create(int kind)
        {
            if (kind == 0) return new RestfulHttpRequest();
            if (kind == 1) return new FileHttpRequest();

            return null;
        }


        public void GetAsync(string[] urls, Action<double> progress = null, Action<List<string>> completed = null)
        {
            lock (mutex)
            {
                Task.Factory.StartNew((args) =>
                {
                    Action<double> action1 = null;
                    Action<List<string>> action2 = null;

                    if (args != null || args.GetType() == typeof(object[]))
                    {
                        object[] actions = args as object[];
                        if (actions.Length > 0) action1 = actions[0] as Action<double>;
                        if (actions.Length > 1) action2 = actions[1] as Action<List<string>>;
                    }

                    Dictionary<long, double> progresses = new Dictionary<long, double>();
                    Dictionary<long, string> filenames = new Dictionary<long, string>();
                    foreach (var item in urls)
                    {
                        var client = new FileHttpRequest();

                        client.Progress += (url, e) =>
                        {
                            var fileArgs = e as FileTransferArgs;
                            progresses.AddOrUpdate(e.RequestId, fileArgs.Progress);

                            if (progresses.Values.Any() && action1 != null)
                            {
                                double avg = progresses.Values.Average();
                                action1?.Invoke(avg);
                            }
                        };

                        client.Completed += (url, e) =>
                        {
                            var fileArgs = e as FileTransferArgs;
                            filenames.AddOrUpdate(e.RequestId, fileArgs.FileName);
                            double avg = progresses.Values.Average();
                            if (avg == 1 && action2 != null) action2?.Invoke(filenames.Values.ToList());
                        };

                        client.SendAsync(item);
                    }

                }, new object[] { progress, completed });
            }
        }
    }
}
